# _*_ coding:utf-8 _*_
# @Time  : 2023.05.23
# @Author: zizlee
import datetime
import pathlib
import json
import sys
import time

import requests
from urllib3 import disable_warnings
import pandas as pd
from WindPy import w

disable_warnings()


# 日志
class Logger:

    @classmethod
    def fetal(cls, msg):
        print(f'\033[91mfetal:{msg}\033[0m')

    @classmethod
    def success(cls, msg):
        print(f'\033[92msuccess:{msg}\033[0m')


# 指标管理
class IndexHandler:
    def __init__(self, index_id: int = None):
        self._index_id = index_id

    def set_index_id(self, iid):
        self._index_id = int(iid)

    def get_index_description(self):
        url = 'https://210.13.218.130:9000/rz/v1/admin/dataindex/{}/?page=1&page_size=2'.format(self._index_id)
        r = requests.get(url, verify=False)
        return r.json()['data']['description']

    def save_data_list(self, data_list, date_key: str, value_key: str):
        new_values = [{'datadate': r[date_key], 'datavalue': r[value_key]} for r in data_list]
        new_values.sort(key=lambda x: x['datadate'])  # 升序
        url = 'http://210.13.218.130:9001/v1/admin/dataindex/{}/'.format(self._index_id)
        r = requests.post(url, json=new_values, verify=False)
        return r.json()['message']

    def update_end(self, days=0, custom: str = None):
        if custom:
            return custom
        t = datetime.datetime.today() + datetime.timedelta(days=days)
        return t.strftime('%Y-%m-%d')

    def read_update_config(self, extra: str = None, frequency: str = None, min_id=1, max_id=-1, point_ids=None):
        config_df = pd.read_csv('更新配置.csv', encoding='utf')
        config_df.fillna('', inplace=True)
        config_df['ID'] = config_df['ID'].apply(lambda x: int(x))
        config_df.sort_values(by='ID', inplace=True)
        config_df = config_df[config_df['ID'] >= min_id]
        if max_id > -1:
            config_df = config_df[config_df['ID'] <= max_id]
        if extra:
            config_df = config_df[config_df['UPDATE_TYPE'] == extra]
        if frequency:
            config_df = config_df[config_df['FR'] == frequency]
        if point_ids:
            config_df = config_df[config_df['ID'].isin(point_ids)]
        # config_df = config_df[config_df['NAME'].str.startswith('期货前20名')]
        return config_df.to_dict(orient='records')

    def get_exist_id(self, kw):
        url = 'https://210.13.218.130:9000/v1/datalib/table/'
        params = {'kw': kw}
        r = requests.get(url, params=params, verify=False)
        print(r.json())
        records = r.json()['data']['records']
        if len(records) == 1:
            return records[0]['id']

    def modify_config(self):
        config_df = pd.read_csv('更新配置.csv', encoding='utf')
        config_df.fillna('', inplace=True)
        new = []
        for r in config_df.to_dict(orient='records'):
            # if r['UPDATE_TYPE'] == 'THS指标' and r['THS_ID'] and not r['SELF_ID']:
            #     r['SELF_ID'] = self.get_exist_id(kw=r['THS_ID'])
            #     time.sleep(1)
            # if r['UPDATE_TYPE'] == 'WIND指标':
            #     if not r['WIND_ID']:
            #         r['WIND_ID'] = r['THS_ID']
            #         r['THS_ID'] = ''
            #     if not r['SELF_ID'] and r['WIND_ID']:
            #         r['SELF_ID'] = self.get_exist_id(kw=r['WIND_ID'])
            #         time.sleep(1)
            new.append(r)
        ndf = pd.DataFrame(new)
        ndf = ndf[['ID','ACTIVE','UPDATE_TYPE','DATA_KEY','FR','THS_ID','THS_CODE','WIND_ID','WIND_CODE','VARIETY','SELF_ID','FORMULA', 'NAME']]
        ndf['ID'] = ndf['ID'].apply(lambda x: '%06d' % x)
        ndf['SELF_ID'] = ndf['SELF_ID'].apply(lambda x: '%d' %x if x else '')
        ndf.to_csv('更新配置2.csv', encoding='utf8', index=False)


# 自己的数据接口
class CustomApi:
    def __init__(self):
        pass

    def get_monthly_quote(self, start_date: str, end_date: str, variety, cur_month: str, value_key: str, contract=''):
        if not variety:
            raise ValueError('品种代码不能为空!')
        url = 'https://210.13.218.130:9000/v1/quote/variety/daily/'
        query = {
            'start': start_date.replace('-', ''),
            'end': end_date.replace('-', ''),
            'variety': variety,
            'contract': contract
        }
        r = requests.get(url, params=query, verify=False)
        quote_data = r.json()['data']
        quote_data = list(filter(lambda x: x['contract'].endswith(cur_month) and x['contract'].startswith(variety), quote_data))
        if len(quote_data) < 1:
            return []
        new_value_df = pd.DataFrame(quote_data)[['quote_date', 'contract', value_key]]
        # 排序，去重，保留第一个
        new_value_df['c_month'] = new_value_df['contract'].apply(lambda x: x[-2:])
        new_value_df.sort_values(by=['quote_date', 'contract'], inplace=True)
        new_value_df.drop_duplicates(subset=['quote_date', 'c_month'], keep='first', inplace=True)
        del new_value_df['contract']
        del new_value_df['c_month']
        if new_value_df.empty:
            return []
        return new_value_df.to_dict(orient='records')

    def get_variety_position(self, start_date: str, end_date: str, variety: str):
        url = 'https://210.13.218.130:9000/v1/datan/quotes/position/rank20/'
        query = {
            'start_date': start_date, # yyyy-MM-dd
            'end_date': end_date,
            'v': variety
        }
        r = requests.get(url, params=query, verify=False)
        position_data = r.json()['data']
        pos_data = []
        for r in position_data['rank_data']:
            pos_data.append({
                'quote_date': r['quote_date'],
                'net_position': r['net_position'],
                'v': r['variety_en']
            })
        return pos_data

    def get_myself_database(self, iid: int, start_date: str, end_date: str):
        url = "https://210.13.218.130:9000/v1/datalib/table/sequence/"
        params = {'iid': int(iid), 'start': start_date, 'end': end_date}
        r = requests.get(url, params=params, verify=False)
        return r.json()['data']['data']

    def formula_index_value(self, start_date: str, end_date: str, formula: str):
        url = 'http://210.13.218.130:9001/v1/admin/dataindex/formulaCalculate/'
        r = requests.post(url, json={'formula': formula, 'start_date': start_date, 'end_date': end_date})
        try:
            ret_data = r.json()['data']['data']
        except Exception:
            print(r.json())
            return []
        else:
            return ret_data

    @staticmethod
    def reset_rank20_start_date(ven, rank_ts=0):
        r = requests.get(f'https://210.13.218.130:9000/v1/datan/quotes/position/rank20/startDate/',
                         params={'v': ven, 'ts': rank_ts},
                         verify=False)
        return r.json()['data']['start_date']

    def get_rank20_position(self, start_date: str, variety: str):
        url = 'https://210.13.218.130:9000/v1/datan/quotes/position/rank20/totalVolume/'
        r = requests.get(url, params={'start_date': start_date, 'v': variety}, verify=False)
        rep_data = r.json()['data']['data_list']
        for i in rep_data:
            i['net_position'] = int(i['t_long_position'] - i['t_short_position'])
        return rep_data


# 同花顺接口API
class THSApi(object):
    REFRESH_TOKEN = 'eyJzaWduX3RpbWUiOiIyMDIyLTEwLTEzIDE0OjQ5OjE5In0=.eyJ1aWQiOiI1NjYxMTYwMTQifQ==.D50AAA1A013854A7092B870411E59F50DDA78A6ABFECA8F2FBFA8BB83C3C77E7'
    TOKEN_EXPIRED = -1302

    def __init__(self):
        self.access_token = None
        self.configs = {}
        self.read_configs()

    def read_configs(self):
        cf = pathlib.Path('access_token.json')
        if cf.exists():
            with open(cf, 'r', encoding='utf8') as f:
                self.configs = json.load(f)
                self.access_token = self.configs.get('THS')

    def set_configs(self, new_token):
        cf = pathlib.Path('access_token.json')
        if cf.exists():
            with open(cf, 'r', encoding='utf8') as f:
                configs = json.load(f)
                configs['THS'] = new_token
            with open(cf, 'w', encoding='utf8') as f2:
                json.dump(configs, f2, indent=2, ensure_ascii=False)

    def get_access_token(self):
        if self.access_token:
            return self.access_token
        self.read_configs()
        if self.access_token:
            return self.access_token
        return self.request_access_token()  # 网络获取

    # 获取access_token
    def request_access_token(self):
        # 相当于登录接口，access_token7天有效，到期前3小时获取会刷新
        access_token_url = 'https://ft.10jqka.com.cn/api/v1/get_access_token'
        try:
            access_response = requests.get(url=access_token_url, headers={
                'ContentType': 'application/json',
                'refresh_token': self.REFRESH_TOKEN
            })
            self.access_token = json.loads(access_response.content)['data']['access_token']
            self.set_configs(self.access_token)
            Logger.success(msg=f'AccessToken变更:{self.access_token}')
        except Exception as e:
            Logger.fetal(msg='同花顺获取AccessToken失败:{}'.format(e))
            raise ValueError('同花顺AccessToken错误!')
        else:
            return self.access_token

    # 历史行情
    def get_history_quotation(self, codes, indicators, start_date, end_date):
        # 请求数据
        url = "https://quantapi.51ifind.com/api/v1/cmd_history_quotation"
        headers = {
            'ContentType': 'application/json',
            'access_token': self.get_access_token()
        }
        form = {
            'codes': ','.join(codes),
            'indicators': ','.join(indicators),
            'startdate': start_date,
            'enddate': end_date
        }
        try:
            r = requests.post(url, json=form, headers=headers)
            res_data = r.json()
            if str(res_data['errorcode']) == str(self.TOKEN_EXPIRED):
                self.access_token = self.request_access_token()
                return self.get_history_quotation(codes, indicators, start_date, end_date)
            if res_data['errorcode'] != 0:
                raise ValueError(f'{res_data["errorcode"]}:{res_data["errmsg"]}')
        except Exception as e:
            Logger.fetal(f'请求历史行情失败:{e}')
            return []
        else:
            quotations = self.to_dict_multi(res_data['tables'])
            # Logger.success('获取{}_{}-{}历史行情{}成功!'.format(start_date, end_date, ','.join(codes), ','.join(indicators)))
            if len(codes) == 1:
                return quotations[codes[0]]
            return quotations

    # 经济数据库
    def get_edb_service(self, indicators, start_date, end_date):
        # 请求数据
        url = "https://ft.10jqka.com.cn/api/v1/edb_service"
        headers = {
            'ContentType': 'application/json',
            'access_token': self.get_access_token()
        }
        form = {
            'indicators': ','.join(indicators),
            'startdate': start_date,
            'enddate': end_date
        }
        try:
            r = requests.post(url, json=form, headers=headers)
            res_data = r.json()
            if str(res_data['errorcode']) == str(self.TOKEN_EXPIRED):
                self.access_token = self.request_access_token()
                return self.get_edb_service(indicators, start_date, end_date)
            if res_data['errorcode'] != 0:
                raise ValueError(f'{res_data["errorcode"]}:{res_data["errmsg"]}')
        except Exception as e:
            Logger.fetal(f'请求EDB情失败:{e}')
            return []
        else:
            ret = self.edb_to_dict(res_data['tables'])
            # Logger.success('获取{}经济数据库数据成功!'.format(','.join(indicators)))
            if len(indicators) == 1:
                return ret[indicators[0]]
            return ret

    # 基本数据
    def get_basic_data_service(self):
        url = "https://ft.10jqka.com.cn/api/v1/basic_data_service"

    def edb_to_dict(self, tables):
        ret_data = {}
        for t_data in tables:
            value_list = []
            value_id = t_data['id'][0]
            for tm, tv in zip(t_data['time'], t_data['value']):
                value_list.append({'id': value_id, 'time': tm, 'value': tv})
            ret_data[value_id] = value_list
        return ret_data

    def to_dict(self, tables):
        res_data = []
        for row in tables:
            _obj = {
                'code': row['thscode'],
            }
            _obj.update({k: v[0] for k, v in row['table'].items()})
            res_data.append(_obj)
        return res_data

    def to_dict_multi(self, tables):
        quotation = {}
        for t_data in tables:
            code_data = []
            for i, t in enumerate(t_data['time']):
                day_obj = {
                    'code': t_data['thscode'],
                    'time': t
                }
                for key, v_list in t_data['table'].items():  # key: [value_list]
                    day_obj[key] = v_list[i]
                code_data.append(day_obj)
            quotation[t_data['thscode']] = code_data
        return quotation


# 万得接口API
class WINDApi(object):
    def __init__(self):
        w.start()
        if not w.isconnected():
            print('WindPy连接失败!')

    def get_edb_service(self, indicators, start_date, end_date):
        ret = w.edb(indicators, start_date, Fill='Previous', usedf=False)
        print(ret)
        return []


def delete_index(delete_config_list):
    for row in delete_config_list:
        print(row)
        token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjIsInJvbGVfY29kZSI6OTk5OSwib3V0X3JvbGUiOjI1NSwiZXhwIjoxNjkwNTAzOTc0Ljc0NjU2OTJ9.eTh24LN7wdGvnBDYEbqyGPA59EpRWC9rtZhgiE54cuM'
        r = requests.delete(f'http://210.13.218.130:9001/v1/admin/dataindex/{row["ID"]}/',
                            headers={'Ruizy-Token': token},
                            verify=False)
        time.sleep(1)


def main_updating(update_config_list, update_end_date):
    ths_api = THSApi()
    # wind_api = WINDApi()
    custom_api = CustomApi()
    tn = len(update_config_list)
    for sn, row in enumerate(update_config_list, 1):
        if not row['ACTIVE']:
            continue
        # 获取本行的数据
        index_handler.set_index_id(row['ID'])
        dest = index_handler.get_index_description()
        start_point = dest['end_date'] if dest['end_date'] else dest['start_date']
        if start_point > update_end_date:
            continue
        # 将自己系统有的数据采用自主系统内取数，省接口的数据量
        if row['SELF_ID']:
            row['UPDATE_TYPE'] = '自己系统'
        rm = ''
        if row['UPDATE_TYPE'] == '合约行情':
            month = '%02d' % int(row['NAME'].rsplit(':', 1)[-1].replace('月', ''))
            quote_key = 'close_price' if row['DATA_KEY'] == 'close' else 'nnn'
            new_data = custom_api.get_monthly_quote(start_date=start_point, end_date=update_end, variety=row['VARIETY'],
                                                    cur_month=month, value_key=quote_key)
            rm = index_handler.save_data_list(new_data, date_key='quote_date', value_key='close_price')
        elif row['UPDATE_TYPE'] == '持仓量':
            new_data = custom_api.get_variety_position(start_date=start_point, end_date=update_end,
                                                       variety=row['VARIETY'])
            rm = index_handler.save_data_list(new_data, date_key='quote_date', value_key='net_position')
        elif row['UPDATE_TYPE'] == '前20排名':
            new_data = custom_api.get_rank20_position(start_date=start_point, variety=row['VARIETY'])
            if row['DATA_KEY'] == 'net_position':
                rm = index_handler.save_data_list(new_data, date_key='rank_date', value_key='net_position')
            elif row['DATA_KEY'] == 'long_position':
                rm = index_handler.save_data_list(new_data, date_key='rank_date', value_key='t_long_position')
            elif row['DATA_KEY'] == 'short_position':
                rm = index_handler.save_data_list(new_data, date_key='rank_date', value_key='t_short_position')
            elif row['DATA_KEY'] == 'trade_volume':
                rm = index_handler.save_data_list(new_data, date_key='rank_date', value_key='t_trade')
            else:
                break
        elif row['UPDATE_TYPE'] == '自己系统':
            new_data = custom_api.get_myself_database(iid=row['SELF_ID'], start_date=start_point, end_date=update_end)
            rm = index_handler.save_data_list(new_data, date_key='datadate', value_key='datavalue')
        elif row['UPDATE_TYPE'] == '系统计算':  # 使用自己系统接口计算数据
            new_data = custom_api.formula_index_value(start_date=start_point, end_date=update_end, formula=row['FORMULA'])
            rm = index_handler.save_data_list(new_data, date_key='datadate', value_key='formula_value')
        elif row['UPDATE_TYPE'] == '历史行情':
            ths_code = row['THS_CODE']
            start_point = dest['end_date'] if dest['end_date'] else dest['start_date']
            if ths_code:
                quotation = ths_api.get_history_quotation(codes=[row['THS_CODE']], indicators=[row['DATA_KEY']], start_date=start_point, end_date=update_end)
                rm = index_handler.save_data_list(quotation, date_key='time', value_key=row['DATA_KEY'])
        elif row['UPDATE_TYPE'] == 'THS指标':
            new_data = ths_api.get_edb_service(indicators=[row['THS_ID']], start_date=start_point, end_date=update_end)
            rm = index_handler.save_data_list(new_data, date_key='time', value_key='value')
        elif row['UPDATE_TYPE'] == 'WIND指标':
            # new_data = wind_api.get_edb_service(indicators=[row['THS_ID']], start_date=start_point, end_date=update_end)
            pass
        Logger.success(f'{sn}/{tn}\t{rm}')

        time.sleep(0.5)


if __name__ == '__main__':
    while True:
        if datetime.datetime.now().strftime('%H:%M') == '02:02' and datetime.datetime.today().weekday() <= 5:
            index_handler = IndexHandler()
            # index_handler.modify_config()
            # sys.exit(0)
            update_end = index_handler.update_end(days=-1)
            # extra表示更新哪种类型的数据 frequency表示更新哪种频率的数据[日周月季年],point_id_list表示指定更新哪些个数据
            # point_ids = [1039, 1103, 1283, 1285]  # [美元兑人民币, 阿曼原油现货价, 普氏(Platts):燃料油(380CST):新加坡, 燃料油(低硫0.5%,FOB):新加坡:中间价]
            point_ids = None
            config_list = index_handler.read_update_config(extra=None, frequency=None, min_id=1, max_id=-1, point_ids=point_ids)
            # config_list = list(filter(lambda x: x['UPDATE_TYPE'] == '历史行情', config_list))
            main_updating(config_list, update_end)  # 启动更新

            # flex_updates = [(1, 61), (1455, 1455), (7780, 7780), (8293, 8293), (8568, 8568), (8584, 8584),
            #             (597, 645), (8514, 8519), (8585, 8585),
            #             (8458, 8513),
            #             (8586, 8640)]
            # for ids in flex_updates:
            #     config_list = index_handler.read_update_config(extra=None, frequency=None, min_id=ids[0], max_id=ids[1], point_ids=None)
            #     main_updating(config_list, update_end)  # 启动更新
            #     time.sleep(20)
